---
title: Events
description: Events allow you to respond to a drag and drop operation.
---

Events fire based on specific user actions and the part of Pragmatic drag and drop involved in the
interaction (such as a `draggable` and drop target).

To listen for all events for a specific type of draggable item (element or file), you can use a
[monitor](/components/pragmatic-drag-and-drop/core-package/monitors).

## Available events

- `onGenerateDragPreview` - Drag is about to start. Make changes you want to see in the drag
  preview.
- `onDragStart` - Something has started dragging. You can make visual changes and they _will not_ be
  captured in the drag preview.
- `onDropTargetChange` - The drop target hierarchy has changed in some way.
- `onDrag` - (_throttled_) - High fidelity updates throughout a drag with the latest details about
  the drag and user input.
- `onDrop` - Drag operation completed.

## Derived events on drop targets

- `onDragEnter` (derived from `onDropTargetChange`) - a drop target is being entered into.
- `onDragLeave` (derived from `onDropTargetChange`) - a drop target is being exited from.

Derived drop target events are not their own events in the system; rather they are derived from
other events. If a drop target wants to know about child drop target changes, then please use the
`onDropTargetChange` event.

## Event ordering

All events flow through the system in the same way:

1. drag source (e.g. `draggable`) if relevant
2. drop targets - inner most upwards (bubble ordering) `grandChild → child → parent`
3. monitors (in the order that they were created)

```ts
const unbind = combine(
	monitorForElements({
		onDragStart: () => 'monitor:start',
	}),
	draggable({
		element: myElement,
		onDragStart: () => 'draggable:start',
	}),
	dropTargetForElements({
		element: myElement,
		onDragStart: () => 'dropTarget:start',
	}),
);

// After `onDragStart`
// console.log:
// - 'draggable:start'
// - 'dropTarget:start',
// - 'monitor:start',
```

## Shared event payload

All events are provided the following base data. Particular event types add additional data.
Additionally, event listeners (e.g. `dropTargetForExternal({ onDragStart })`) can be given
additional localized data for convenience.

```ts
import { Input, DragLocation, DragLocationHistory } from '@atlaskit/pragmatic-drag-and-drop/types';
```

```ts
export type Input = {
	// user input
	altKey: boolean;
	button: number;
	buttons: number;
	ctrlKey: boolean;
	metaKey: boolean;
	shiftKey: boolean;

	// coordinates
	clientX: number;
	clientY: number;
	pageX: number;
	pageY: number;
};

export type DragLocation = {
	/**
	 * A users input at a point in time
	 */
	input: Input;
	/**
	 * A _bubble_ ordered (innermost upwards) list of active drop targets
	 *
	 * @example
	 * [grandChildRecord, childRecord, parentRecord]
	 *
	 */
	dropTargets: DropTargetRecord[];
};

export type DragLocationHistory = {
	/**
	 * Where the drag operation started
	 */
	initial: DragLocation;
	/**
	 * Where the user currently is
	 */
	current: DragLocation;
	/**
	 * Where the user was previously.
	 * `previous` points to what `current` was in the last dispatched event
	 *
	 * `previous` is particularly useful for `onDropTargetChange`
	 * (and the derived `onDragEnter` and `onDragLeave`)
	 * as you can know what the delta of the change
	 *
	 * Exception: `onGenerateDragPreview` and `onDragStart` will have the
	 * same `current` and `previous` values. This is done so that the data
	 * received in `onDragStart` feels logical
	 * (`location.previous` should be `[]` in `onDragStart`)
	 */
	previous: Pick<DragLocation, 'dropTargets'>;
};

// Each drag type (eg files) has their own base payload type
// This allows different types of drags to have different source data
type BaseEventPayload = {
	location: DragLocationHistory;
	// source is different for different drag types
	source: {
		element: Element;
		dragHandle: Element | null;
		data: Record<string, unknown>;
	};
};

import { ElementBaseEventPayload } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
```

## Convenience data for `dropTarget`s (of all types)

All _drop targets_ add a `self` property (type: `DropTargetRecord`) that contains a convenience
object with information about the drop target that the event is firing on.

You could get all this information from the outer scope (or from `location.current.dropTargets`),
but having it available inline is convenient.

```ts
type AllowedDropTargetDropEffect = Exclude<DataTransfer['dropEffect'], 'none'>;

type DropTargetRecord = {
	element: Element;
	// data provided using .getData()
	data: Record<string | symbol, unknown>;
	// dropEffect provided by using getDropEffect()
	dropEffect: AllowedDropTargetDropEffect;
};
```

```ts
dropTargetForExternal({
	element: myElement,
	getData: () => ({ name: 'Alex' }),
	getDropEffect: () => 'move',
	onDragStart: ({ self }) => {
		console.log(self.element); // myElement
		console.log(self.data); // {name: 'Alex'}
		console.log(self.dropEffect); // 'move'
	},
});
```

## External drag sources

Sometimes a user is dragging something that started outside the current browser window, for example
a local file.

When a user first drags an external entity into the webpage, `@atlaskit/pragmatic-drag-and-drop`
considers this the _start_ of the drag operation. The drag operation will _finish_ if the user drags
the external entity out of the browser.

By following this model, all entity types follow the same event lifecycle.

## Event: `onGenerateDragPreview`

You can make changes to the DOM that you want to be reflected in your _drag preview_.

```ts
BaseEventPayload & {
  // Allows people to use the native set drag image function if they want
  // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage
  // Although, we recommend using alternative techniques (see element source docs)
  nativeSetDragImage: DataTransfer['setDragImage'] | null;
  };
```

Flow

- `onGenerateDragPreview` fires on the drag source (eg a `draggable`)
- `onGenerateDragPreview` on the any parent drop targets of the drag sources in bubble order
  (innermost upwards)
- `onGenerateDragPreview` on _monitors_

## Event: `onDragStart`

A drag operation has started. You can make changes to the DOM and those changes won't be reflected
in your _drag preview_.

Flow

- `onDragStart` fires on the drag source (eg a `draggable`)
- `onDragStart` on the any parent drop targets of the drag sources in bubble order (innermost
  upwards)
- `onDragStart` on _monitors_

## Event: `onDrag`

A throttled update of where the the user is currently dragging. Useful if you want to create a high
fidelity experience such as drawing. `@atlaskit/pragmatic-drag-and-drop` throttles native `drag`
events using `requestAnimationFrame` so `onDrag` events will fire around 60 times a second.

Flow

- `onDrag` fires on the drag source (eg a `draggable`)
- `onDrag` on the _current_ parent drop targets of the drag sources in bubble order (innermost
  outwards). The _current_ drop targets are the drop targets that the user is currently dragging
  over. If you want to listen for _all_ `onDrag` events from the _initial_ drop targets, you can use
  a _monitor_.
- `onDrag` on _monitors_

## Event: `onDropTargetChange`

The `onDropTargetChange` event fires when the `dropTarget` hierarchy changes during a drag.

In a single native drag event:

- drop targets can be exited
- drop targets can be entered into
- the hierarchy of drop targets can change (such as by dynamically adding a new parent `dropTarget`
  during a drag - although you are unlikely to run into this one!)

Flow (high level)

- `onDropTargetChange` fires on the drag source (eg a `draggable`)
- `onDropTargetChange` fires on all _previous_ drop targets in bubble order (inside out)
- `onDropTargetChange` fires on any **new** _current_ drop targets in bubble order

> Scenario: [B, A] → [C, A]

- `onDropTargetChange` fires on `draggable`
- `onDropTargetChange` fires on `dropTarget(B)`
- `onDropTargetChange` fires on `dropTarget(A)`
- _now going to fire on newly added drop targets in bubble order_
- `onDropTargetChange` fires on `dropTarget(C)`

> Scenario: [B, A] → [D, C]

- `onDropTargetChange` fires on `draggable`
- `onDropTargetChange` fires on `dropTarget(B)`
- `onDropTargetChange` fires on `dropTarget(A)`
- _now going to fire on newly added drop targets in bubble order_
- `onDropTargetChange` fires on `dropTarget(D)`
- `onDropTargetChange` fires on `dropTarget(C)`
- `onDropTargetChange` fires on all monitors

### Derived events: `onDragEnter` and `onDragLeave`

The `onDropTargetChange` event allows you to know a lot of information about drop target hierarchy
changes. One of the most common use cases for a `dropTarget` is to know when it starts being dragged
over, and when it is no longer being dragged over. To support this common use case, the
`onDragEnter` and `onDragLeave` callbacks are executed on a `dropTarget` as required when the
`dropTarget` receives a `onDropTargetChange` event.

The `onDragEnter` and `onDragLeave` events are not _strictly_ their own events, but travel along
side the bubbling of `onDropTargetChange`.

> Scenario: [B, A] → [C, A]

- `onDropTargetChange` fires on `draggable`
- `onDropTargetChange` fires on `dropTarget(B)`
  - `onDragLeave` fires on `dropTarget(B)` → _derived event_
- `onDropTargetChange` fires on `dropTarget(A)`
- `onDropTargetChange` fires on `dropTarget(C)`
  - `onDragEnter` fires on `dropTarget(C)` → _derived event_
- `onDropTargetChange` fires on all monitors

> Scenario: [B, A] → [D, C]

- `onDropTargetChange` fires on `draggable`
- `onDropTargetChange` fires on `dropTarget(B)`
  - `onDragLeave` fires on `dropTarget(B)` → _derived event_
- `onDropTargetChange` fires on `dropTarget(A)`
  - `onDragLeave` fires on `dropTarget(A)` → _derived event_
- _now going to fire on newly added drop targets in bubble order_
- `onDropTargetChange` fires on `dropTarget(D)`
  - `onDragEnter` fires on `dropTarget(D)` → _derived event_
- `onDropTargetChange` fires on `dropTarget(C)`
  - `onDragEnter` fires on `dropTarget(C)` → _derived event_
- `onDropTargetChange` fires on monitors

## Event: `onDrop`

The `onDrop` event occurs when a user has finished a drag and drop operation. The `onDrop` event
will fire when the drag operation finishes, regardless of how the drag operation finished (e.g. due
to an explicit drop, the drag being canceled, recovering from an error and so on).

Using the information that the web platform provides, we cannot distinguish between dropping on no
drop targets, an explicit cancel, or dropping externally, so we do not publish any information about
_how_ the drag ended, only that it ended. The `location.current` property will accurately contain
the final drop targets.

Flow

- `onDrop` fires on the drag source (eg a `draggable`)
- `onDrop` fires on all `dropTarget`s currently being dragged over (all those in
  `location.current.dropTargets`) in bubble event ordering (inner most `dropTarget` upwards)
- `onDrop` fires on _monitors_

For drags that start within the current `window`
([element adapter](/components/pragmatic-drag-and-drop/core-package/adapters/element),
[text selection adapter](/components/pragmatic-drag-and-drop/core-package/adapters/text-selection)),
the drag finishes when the drag operation finish, which might be while the user is outside of the
current `window`. For drags that start outside of the current `windows`
([external adapter](/components/pragmatic-drag-and-drop/core-package/adapters/external)), a drag
finishes when the user leaves the `window`.

## Cancelling

If a user is currently over drop target(s) when a drag is cancelled, then the exiting current drop
targets(s) are cleared with an `onDropTargetChange` event. This matches how cancelling works with
the browser's drag and drop API.

Flow

- User drags over two drop targets: `[B, A]` (bubble ordered)
- User cancels drag
- Phase 1: `onDropTargetChange`
  - `onDropTargetChange` fires on the source (eg a `draggable`)
  - `onDropTargetChange` fires on `B`
    - `onDragLeave` fires on `B` (derived event)
  - `onDropTargetChange` fires on `A`
    - `onDragLeave` fires on `A` (derived event)
  - `onDropTargetChange` fires on _monitors_
- Phase 2: `onDrop`
  - `onDrop` fires on the source (eg a `draggable`)
  - `onDrop` fires on _monitors_
  - _note: `onDrop` does not fire on `B` or `A` as they are no longer being dragged over_
